#include <winhack.h>
#include <elasys_server.h>
#include "ime.h"

#define START_IME 1
#define STOP_IME 2
#define CONTROL_ABORT_READ 3

static CIME * g_Ime = NULL;

void _IME_Enqueue(char* str,int len)
{
    if(NULL == g_Ime) return ;
    MemoryBuf buf((BYTE*)str,len);
    g_Ime->Write(0,buf,NULL,NULL);
}

ECode CIME::Read(
        /* [in] */  Int64 offset,
        /* [in] */  Int32 numberOfBytesToRead,
        /* [out] */ MemoryBuf * pData,
        /* [out] */ IEvent * * ppCompletionEvent)
{
    if(!m_isRunning)
        return E_INVALID_OPERATION;
    int ret = -1;
    Int32 inputStrAddr;
    while (0 != ret && m_isRunning) {
        inputStrAddr=NULL;
        ret = m_inputStrQueue.Dequeue(&inputStrAddr);
        if (0 == ret && inputStrAddr) {
            BYTE* buffer=(BYTE*)(inputStrAddr+sizeof(Int32));
            Int32* len=(Int32*)inputStrAddr;
            pData->Copy(buffer,*len);
            free((char*)inputStrAddr);
            return NOERROR;
        }
        WaitForSingleObjectEx(m_hEvent, (DWORD)INFINITE, FALSE);
        if(m_readAbortFlag)
        {
            m_readAbortFlag=false;
            return E_TIMED_OUT;
        }
    }
    return E_TIMED_OUT;
}

ECode CIME::Write(
        /* [in] */  Int64 offset,
        /* [in] */ const  MemoryBuf & ebbData,
        /* [out] */ Int32 * pNumberOfBytesWritten,
        /* [out] */ IEvent * * ppCompletionEvent)
{
    if(!m_isRunning)
        return NOERROR;
    char *str=(char *)ebbData.GetPayload();
    int len=ebbData.GetUsed();
    char* strBuf=(char*)malloc(len+sizeof(Int32));
    *((Int32*)strBuf)=len;
    memcpy(strBuf+sizeof(Int32),str,len);
    m_inputStrQueue.Enqueue(&strBuf);
    SetEvent(m_hEvent);
    return NOERROR;
}

ECode CIME::Control(
        /* [in] */  Handle32 controlCode,
        /* [in] */ const  MemoryBuf & ebbInData,
        /* [out] */ MemoryBuf * pOutData,
        /* [out] */ IEvent * * ppCompletionEvent)
{
    switch(controlCode){
        case START_IME:{
            m_isRunning=true;
            return NOERROR;
        }
        case STOP_IME:{
            m_isRunning=false;
            return NOERROR;
        }
        case CONTROL_ABORT_READ:{
            m_readAbortFlag=true;
            SetEvent(m_hEvent);
            return NOERROR;
        }
    }
    return E_INVALID_ARGUMENT;
}

void CIME::Dispose()
{
}

CIME::~CIME()
{
    if (NULL != m_hEvent) {
        CloseHandle(m_hEvent);
        m_hEvent = NULL;
    }
    
    Int32 inputStrAddr;
    while (0 == m_inputStrQueue.Dequeue(&inputStrAddr)) {
        if (inputStrAddr) {
            free((char*)inputStrAddr);
        }
    }
}

ECode CIME::Initialize()
{
    m_isRunning=false;
    m_hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
    if (NULL == m_hEvent) {
         // todo ERROR;
         return E_FAIL;
    }
    m_readAbortFlag=false;
    return NOERROR;
}

ECode CIME::S_Create(
        /* [in] */ Int32 environmentId,
        /* [out] */ IDeviceDriver ** ppIme)
{
    CIME * pIme = NULL;
    ECode ec = NOERROR;

    pIme = new CIME(environmentId);
    if (NULL == pIme) {
        return E_OUT_OF_MEMORY;
    }
    ec = pIme->Initialize();
    if (FAILED(ec)) {
        goto ErrorExit;
    }
    pIme->AddRef();

    g_Ime = pIme;
    *ppIme = (IDeviceDriver *)pIme;
    return ec;

ErrorExit:
    if (NULL != g_Ime) {
        delete g_Ime;
        g_Ime = NULL;
    }
    return ec;
}

CIME*  CIME::S_Get(
        /* [in] */ Int32 environmentId)
{
    return g_Ime;
}


IDeviceDriver* CDECL CreateImeDriver(uint_t uDeviceNo, void *pvParameter)
{
    IDeviceDriver* ime=NULL;
    CIME::S_Create(0,&ime);
    return ime;
}
